/**
 * 
 */
package edu.rit.cs.ws.uddi;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;

import org.apache.juddi.v3.client.ClassUtil;
import org.apache.juddi.v3.client.config.UDDIClientContainer;
import org.apache.juddi.v3.client.transport.Transport;
import org.uddi.api_v3.AccessPoint;
import org.uddi.api_v3.AuthToken;
import org.uddi.api_v3.BindingTemplate;
import org.uddi.api_v3.BindingTemplates;
import org.uddi.api_v3.BusinessDetail;
import org.uddi.api_v3.BusinessEntity;
import org.uddi.api_v3.BusinessInfo;
import org.uddi.api_v3.BusinessList;
import org.uddi.api_v3.BusinessService;
import org.uddi.api_v3.CategoryBag;
import org.uddi.api_v3.Contact;
import org.uddi.api_v3.Contacts;
import org.uddi.api_v3.DeleteBusiness;
import org.uddi.api_v3.Description;
import org.uddi.api_v3.Email;
import org.uddi.api_v3.FindBusiness;
import org.uddi.api_v3.GetAuthToken;
import org.uddi.api_v3.GetBusinessDetail;
import org.uddi.api_v3.KeyedReference;
import org.uddi.api_v3.Name;
import org.uddi.api_v3.PersonName;
import org.uddi.api_v3.Phone;
import org.uddi.api_v3.SaveBusiness;
import org.uddi.api_v3.SaveService;
import org.uddi.v3_service.DispositionReportFaultMessage;
import org.uddi.v3_service.UDDIInquiryPortType;
import org.uddi.v3_service.UDDIPublicationPortType;
import org.uddi.v3_service.UDDISecurityPortType;

import edu.rit.cs.web.pojos.Business;

/**
 * Implements the methods used to interact with the UDDI registry. This
 * implementation is particular to jUDDI registry and client.
 * 
 * The connection settings in the jUDDI registry are located in
 * /META-INF/uddi.xml. This is the default setting for the jUDDI client.
 * 
 * @author Abhishek Shrestha (axs1430)
 * @author Juan Saldana (jjs6109)
 * @author Victor Alvarez (vxa1557)
 */
public class UDDIServiceImpl implements UDDIService {
  private static final long serialVersionUID = 1L;

  /**
   * Client to jUDDI security service.
   */
  private static UDDISecurityPortType security = null;
  /**
   * Client to jUDDI publish service.
   */
  private static UDDIPublicationPortType publish = null;
  /**
   * Client to jUDDI inquiry service.
   */
  private static UDDIInquiryPortType inquiry = null;

  static {
    try {
      String clazz = UDDIClientContainer.getUDDIClerkManager(null)
              .getClientConfig().getUDDINode("default").getProxyTransport();
      Class<?> transportClass = ClassUtil.forName(clazz, Transport.class);
      if (transportClass != null) {
        Transport transport = (Transport) transportClass.getConstructor(
                String.class).newInstance("default");

        security = transport.getUDDISecurityService();
        publish = transport.getUDDIPublishService();
        inquiry = transport.getUDDIInquiryService();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  public void publish(Business business) throws DispositionReportFaultMessage, RemoteException {
    AuthToken myPubAuthToken = getPublisherAuthToken();

    // Creating the parent business entity that will contain our service.
    BusinessEntity busEntity = new BusinessEntity();

    // Add the business entity's name
    Name busName = new Name();
    busName.setValue(business.getName());
    busEntity.getName().add(busName);

    // Add the Description
    Description description = new Description();
    description.setValue(business.getDescription());
    busEntity.getDescription().add(description);

    // Add the Contact info;
    Contact contact = new Contact();
    contact.setUseType("Administrative Contact");
    Email email = new Email();
    email.setValue(business.getContact().getEmail());
    contact.getEmail().add(email);
    PersonName pName = new PersonName();
    pName.setValue(business.getContact().getName());
    contact.getPersonName().add(pName);
    Phone phone = new Phone();
    phone.setValue(business.getContact().getPhone());
    contact.getPhone().add(phone);
    busEntity.setContacts(new Contacts());
    busEntity.getContacts().getContact().add(contact);

    busEntity.setCategoryBag(categoryBag());

    // Adding the business entity to the "save" structure, using our
    // publisher's authentication info and saving away.
    SaveBusiness saveBusiness = new SaveBusiness();
    saveBusiness.getBusinessEntity().add(busEntity);
    saveBusiness.setAuthInfo(myPubAuthToken.getAuthInfo());
    BusinessDetail bd = publish.saveBusiness(saveBusiness);
    String myBusKey = bd.getBusinessEntity().get(0).getBusinessKey();

    // Creating a service to save.
    BusinessService service = new BusinessService();
    service.setBusinessKey(myBusKey);
    Name myServName = new Name();
    myServName.setValue(business.getService().getName());
    service.getName().add(myServName);

    description = new Description();
    description.setValue(business.getService().getDescription());
    service.getDescription().add(description);
    

    // Add binding templates, etc...
    BindingTemplate bt = new BindingTemplate();
    AccessPoint point = new AccessPoint();
    point.setUseType("url");
    point.setValue(business.getService().getUrl());
    bt.setAccessPoint(point);

    BindingTemplates tess = new BindingTemplates();
    tess.getBindingTemplate().add(bt);
    service.setBindingTemplates(tess);

    // Adding the service to the "save" structure, using our publisher's
    // authentication info and saving away.
    SaveService ss = new SaveService();
    ss.getBusinessService().add(service);
    ss.setAuthInfo(myPubAuthToken.getAuthInfo());
    publish.saveService(ss);
  }

  /**
   * Creates a category bag to classify the business entity or service. All the
   * category bags created are the same.
   * 
   * @see UDDIService#CATEOGRY
   * @return A category bag with the default category.
   */
  protected CategoryBag categoryBag() {
    final CategoryBag bag = new CategoryBag();
    KeyedReference keyed = new KeyedReference();
    keyed.setKeyName("category");
    keyed.setKeyValue(CATEOGRY);
    keyed.setTModelKey(T_MODEL);
    bag.getKeyedReference().add(keyed);

    return bag;
  }

  @Override
  public void deleteBusiness(final String businessKey) throws DispositionReportFaultMessage, RemoteException {
    DeleteBusiness db = new DeleteBusiness();
    db.getBusinessKey().add(businessKey);
    db.setAuthInfo(getPublisherAuthToken().getAuthInfo());
    publish.deleteBusiness(db);
  }

  /**
   * Retrieves the authorization token for the specified jUDDI publisher.
   * 
   * @return The authorization token used to publish/delete/retrieve business
   *  information.
   * @throws DispositionReportFaultMessage
   * @throws RemoteException
   */
  protected AuthToken getPublisherAuthToken() throws DispositionReportFaultMessage, RemoteException {
    // Retrieve the publisher's uddi authorization token
    GetAuthToken getAuthTokenMyPub = new GetAuthToken();
    getAuthTokenMyPub.setUserID("uddi");
    getAuthTokenMyPub.setCred("");
    AuthToken pubAuthToken = security.getAuthToken(getAuthTokenMyPub);

    return pubAuthToken;
  }

  @Override
  public Business getBusinessDetail(String businessKey) throws DispositionReportFaultMessage, RemoteException {

    GetBusinessDetail bd = new GetBusinessDetail();
    bd.setAuthInfo(getPublisherAuthToken().getAuthInfo());
    bd.getBusinessKey().add(businessKey);

    BusinessDetail results = inquiry.getBusinessDetail(bd);

    int size = results.getBusinessEntity().size();
    Business b = new Business();

    if (size > 0 ) {
      BusinessEntity be = results.getBusinessEntity().get(0);
      b.setKey(businessKey);
      b.setDescription(be.getDescription().get(0).getValue());
  
      Contact contact = be.getContacts().getContact().get(0);
      b.getContact().setEmail(contact.getEmail().get(0).getValue());
      b.getContact().setPhone(contact.getPhone().get(0).getValue());
      b.getContact().setName(contact.getPersonName().get(0).getValue());
  
      BusinessService service = be.getBusinessServices().getBusinessService().get(0);
      b.getService().setKey(service.getBusinessKey());
      b.getService().setDescription(service.getDescription().get(0).getValue());
      b.getService().setName(service.getName().get(0).getValue());
      b.getService().setUrl(service.getBindingTemplates().getBindingTemplate().get(0).getAccessPoint().getValue());
    }
    return b;
  }

  @Override
  public List<Business> findAllBusinesses() throws DispositionReportFaultMessage, RemoteException {

    final FindBusiness fb = new FindBusiness();
    fb.setAuthInfo(getPublisherAuthToken().getAuthInfo());
    fb.setCategoryBag(categoryBag());

    BusinessList bList = inquiry.findBusiness(fb);

    int size = bList.getListDescription().getIncludeCount();
    List<Business> businesses = new ArrayList<Business>(size);
    if (size > 0) {
      for (BusinessInfo bi : bList.getBusinessInfos().getBusinessInfo()) {
        Business b = new Business();
        b.setKey(bi.getBusinessKey());
        b.setDescription(bi.getDescription().get(0).getValue());
        b.setName(bi.getName().get(0).getValue());

        b.getService().setKey(bi.getServiceInfos().getServiceInfo().get(0).getServiceKey());
        b.getService().setName(bi.getServiceInfos().getServiceInfo().get(0).getName().get(0).getValue());
        businesses.add(b);
      }
    }

    return businesses;
  }
}